/*
 * QTextArea.cpp  0.4.0 / Dec 04, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "QTextArea.h"

#include "Cute4jSwing.h"
#include "TextAreaDelegate.h"
#include "SwingGraphics.h"

// QTextArea :: Protected Constructor. -----------------------------------------

/*!
 * \brief Constructs new QTextArea object.
 *
 * \param delegate the TextAreaDelegate delegate
 */
QTextArea::QTextArea(TextAreaDelegate *delegate)
  : QTextEdit(), m_document(0), m_paintView(false), m_delegate(delegate),
    m_paintViewEvent(0)
{
  if (sm_defaultDocumentCount == 0) {
    sm_defaultDocument = document();
    sm_defaultDocument->setParent(0);
  }
  ++sm_defaultDocumentCount;
  setAcceptRichText(false);
  setLineWrapMode(QTextEdit::NoWrap);
  setWordWrapMode(QTextOption::NoWrap);
}

// QTextArea :: Protected Destructor. ------------------------------------------

/*!
 * \brief Destroys the QTextArea object.
 */
QTextArea::~QTextArea()
{
  if (--sm_defaultDocumentCount == 0) {
    delete sm_defaultDocument;
  }
}

// QTextArea :: Protected Member Functions. ------------------------------------

/*!
 * \brief Sets text document.
 *
 * \param document the document
 */
void QTextArea::setDocument(QTextDocumentAdapter *document)
{
  if (m_document != document) {
    if (m_document) {
      disconnectSignals();
    }
    if (document) {
      m_document = document;
      connectSignals();
      QTextEdit::setDocument(document);
    } else {
      QTextEdit::setDocument(sm_defaultDocument);
    }
  }
}

/*!
 * \brief Sets whether or not paints component view.
 *
 * \param paintView \c true if paint
 */
void QTextArea::setPaintView(bool paintView)
{
  if (m_paintView != paintView) {
    if (paintView) {
      m_paintView = true;
      connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this,
              SLOT(valueChangedSlot(int)));
      connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this,
              SLOT(valueChangedSlot(int)));
    } else {
      m_paintView = false;
      disconnect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this,
                 SLOT(valueChangedSlot(int)));
      disconnect(verticalScrollBar(), SIGNAL(valueChanged(int)), this,
                 SLOT(valueChangedSlot(int)));
    }
  }
}

/*!
 * \brief Default view painting.
 *
 * \param graphicsPointer the SwingGraphics pointer
 */
void QTextArea::paintView(jlong graphicsPointer)
{
  if (m_paintView && m_paintViewEvent) {
    swingGraphics->push();
    QTextEdit::paintEvent(m_paintViewEvent);
    swingGraphics->pop();
    // TODO: Move into SwingGraphics the following code
    const int xOffset = horizontalScrollBar()->value();
    const int yOffset = verticalScrollBar()->value();
    swingGraphics->translate(-xOffset, -yOffset);
    QRect clipRect(xOffset, yOffset, viewport()->width(), viewport()->height());
    swingGraphics->setClipRect(clipRect);
  }
}

/*!
 * \brief Connects signals.
 */
void QTextArea::connectSignals()
{
  connect(m_document, SIGNAL(destroying()), SLOT(destroyingSlot()));
  connect(m_document, SIGNAL(textCleared()), SLOT(textClearedSlot()));
  connect(m_document, SIGNAL(textSet()), SLOT(textSetSlot()));
  connect(m_document, SIGNAL(textAppended()), SLOT(textAppendedSlot()));
  connect(this, SIGNAL(undoAvailable(bool)), m_delegate,
          SLOT(undoAvailableSlot(bool)));
  connect(this, SIGNAL(redoAvailable(bool)), m_delegate,
          SLOT(redoAvailableSlot(bool)));
  connect(this, SIGNAL(copyAvailable(bool)), SLOT(copyAvailableSlot(bool)));
  connect(m_document, SIGNAL(selectAllAvailable(bool)),
          SLOT(selectAllAvailableSlot(bool)));
}

/*!
 * \brief Disconnects signals.
 */
void QTextArea::disconnectSignals()
{
  disconnect(m_document, SIGNAL(destroying()), this,
             SLOT(destroyingSlot()));
  disconnect(m_document, SIGNAL(textCleared()), this,
             SLOT(textClearedSlot()));
  disconnect(m_document, SIGNAL(textSet()), this, SLOT(textSetSlot()));
  disconnect(m_document, SIGNAL(textAppended()), this,
             SLOT(textAppendedSlot()));
  disconnect(this, SIGNAL(undoAvailable(bool)), m_delegate,
             SLOT(undoAvailableSlot(bool)));
  disconnect(this, SIGNAL(redoAvailable(bool)), m_delegate,
             SLOT(redoAvailableSlot(bool)));
  disconnect(this, SIGNAL(copyAvailable(bool)), this,
             SLOT(copyAvailableSlot(bool)));
  disconnect(m_document, SIGNAL(selectAllAvailable(bool)), this,
             SLOT(selectAllAvailableSlot(bool)));
}

// QTextArea :: Protected Member Functions. ------------------------------------

/*!
 * \brief \c destroying slot.
 */
void QTextArea::destroyingSlot()
{
  disconnectSignals();
  m_document = 0;
  QTextEdit::setDocument(sm_defaultDocument);
  setReadOnly(true);
}

/*!
 * \brief \c valueChanged slot.
 *
 * \param value the chnaged value
 */
void QTextArea::valueChangedSlot(int value)
{
  Q_UNUSED(value);
  viewport()->update(); // TODO: optimize
}

/*!
 * \brief \c textSet slot.
 */
void QTextArea::textClearedSlot()
{
  m_delegate->cutAvailable(false);
  m_delegate->copyAvailable(false);
  m_delegate->clearAvailable(false);
  m_delegate->selectAllAvailable(false);
  if (!isReadOnly()) {
    setFocus();
  }
}

/*!
 * \brief \c textSet slot.
 */
void QTextArea::textSetSlot()
{
  moveCursor(QTextCursor::Start);
  if (!isReadOnly()) {
    setFocus();
  }
}

/*!
 * \brief \c textAppended slot.
 */
void QTextArea::textAppendedSlot()
{
  moveCursor(QTextCursor::End);
}

/*!
 * \brief \c selectionChanged slot.
 *
 * \param available \c true if available
 */
void QTextArea::copyAvailableSlot(bool available)
{
  if (!isReadOnly()) {
    m_delegate->cutAvailable(available);
    m_delegate->clearAvailable(available);
  }
  m_delegate->copyAvailable(available);
  m_delegate->selectAllAvailable(available);
}

/*!
 * \brief \c selectAllAvailable slot.
 *
 * \param available \c true if available
 */
void QTextArea::selectAllAvailableSlot(bool available)
{
  m_delegate->selectAllAvailable(available);
}

// QTextArea :: Overridden Member Function. ------------------------------------

/*!
 * \inheritDoc
 */
void QTextArea::paintEvent(QPaintEvent *e)
{
  if (m_paintView) {
    const int xOffset = horizontalScrollBar()->value();
    const int yOffset = verticalScrollBar()->value();
    SwingGraphics qsg(viewport());
    qsg.translate(-xOffset, -yOffset);
    QRect clipRect(xOffset, yOffset, viewport()->width(), viewport()->height());
    qsg.setClipRect(clipRect);
    m_paintViewEvent = e;
    m_delegate->paintView(reinterpret_cast<jlong>(&qsg), clipRect.x(),
                          clipRect.y(), clipRect.width(), clipRect.height());
    m_paintViewEvent->accept();
    m_paintViewEvent = 0;
  } else {
    QTextEdit::paintEvent(e);
  }
}

// QTextArea :: Private Static Data Members. -----------------------------------

QTextDocument *QTextArea::sm_defaultDocument = 0;
int QTextArea::sm_defaultDocumentCount = 0;
